import { _decorator, BufferAsset, Component, director, isValid, NodeEventType, UITransform } from 'cc';
import { GComponent } from './GComponent';
import { UIPackage } from './UIPackage';
import { UIPackageManager } from './UIPackageManager';
import { InputProcessor } from './event/InputProcessor';
import GPanel, { IGPanelOwner } from './GPanel';
import { BranchAdapter } from './extension/BranchAdapter';
import { UIDevice } from './UIDevice';
import { LocalizedUIBase } from './extension/LocalizedUIBase';
const { ccclass, requireComponent, property, disallowMultiple } = _decorator;

@ccclass('UIPanel')
@requireComponent(UITransform)
@disallowMultiple(true)
export class UIPanel extends Component {
    public static Events = {
        ON_UIPANEL_PRE_DESTROY: "on_uipanel_pre_destroy"
    };

    @property(BufferAsset)
    package: BufferAsset;

    @property({
        readonly: true
    })
    packageName: string = '';

    @property
    itemName: string = '';

    @property
    private _clickable: boolean = true;

    private _root: GComponent;
    private _cmp: GComponent;
    private _loaded: boolean;
    private _loadFailed: boolean;
    private _isDestoryed: boolean;

    private _loadPromise: Promise<void>;
    private _owner: IGPanelOwner;

    public set owner(val: IGPanelOwner) {
        this._owner = val;
        if (this._root && this._root instanceof GPanel) {
            this._root.owner = val;
        }
    }

    public get owner(): IGPanelOwner {
        return this._owner;
    }

    public static ChangeBranch(branch: string): void {
        if (UIPackage.branch == branch) return;
        UIPackage.branch = branch;
        const rootNodes = director.getScene()!.children;
        // walk all nodes with branch adtper
        const adapters: BranchAdapter[] = [];
        for (let i = 0; i < rootNodes.length; ++i) {
            let labels = rootNodes[i].getComponentsInChildren('BranchAdapter');
            Array.prototype.push.apply(adapters, labels);
        }
        for (let i = 0; i < adapters.length; ++i) {
            let obj = adapters[i];
            if (!obj.node.active) continue;
            obj.updateBranch();
        }
    }

    public static UpdateLocalized(): void {
        const branch = UIDevice.inst.i18n.t("__fgui_branch__");
        if (branch !== "__fgui_branch__" || branch != null) {
            UIPanel.ChangeBranch(branch);
        }
        const rootNodes = director.getScene()!.children;
        // walk all nodes with localize object and update
        const allLocalizedCmps: LocalizedUIBase<any>[] = [];
        for (let i = 0; i < rootNodes.length; ++i) {
            let labels = rootNodes[i].getComponentsInChildren('LocalizedUIBase');
            Array.prototype.push.apply(allLocalizedCmps, labels);
        }
        for (let i = 0; i < allLocalizedCmps.length; ++i) {
            let obj = allLocalizedCmps[i];
            if (!obj.node.active) continue;
            obj.updateLanguage();
        }
    }

    public get root(): GPanel {
        return this._root.Root;
    }

    public get clickable(): boolean {
        return this._clickable;
    }

    public get loadPromise(): Promise<void> {
        if (this._loaded) {
            return Promise.resolve();
        }

        if (this._loadFailed) {
            return Promise.reject("load ui failed");
        }

        if (!this._loadPromise) {
            this._loadPromise = this.loadAndCreate().catch((err) => {
                console.error(`load ui failed: ${this.packageName}/${this.itemName} ${err}:: ${err instanceof Error ? err.stack : ""}`);
                this._loadFailed = true;
                return Promise.reject(err);
            })
        }
        return this._loadPromise;
    }

    // 动态修改为可点击有坑，无法响应点击
    // public set clickable(val: boolean) {
    //     // if (this._clickable == val) return;
    //     this._clickable = val;
    //     if (val) {
    //         this.initInput();
    //         this._root.inputProcessor.enabled = true;
    //         console.log('>>>>>>>>>>>>>>>. enable');
    //     } else {
    //         this._root.inputProcessor.enabled = false;
    //         console.log('>>>>>>>>>>>>>>>. disable');
    //     }
    // }

    public get Component(): GComponent {
        return this._cmp;
    }

    public setRoot(root: GComponent): void {
        if (this._root) {
            return console.error('root already set!');
        }
        this._root = root;
    }

    private onNodeDestroy(): void {
        this.node.off(NodeEventType.NODE_DESTROYED, this.onNodeDestroy, this);
        this.node.emit(UIPanel.Events.ON_UIPANEL_PRE_DESTROY, this);
        this.node.active = false;
        if (this._root instanceof GPanel)
            this._root.dispose();
    }

    private initRoot(): void {
        if (this._root) return;
        this.node.off(NodeEventType.SIZE_CHANGED, this.onNodeSizeChanged, this)
        this.node.off(NodeEventType.NODE_DESTROYED, this.onNodeDestroy, this);
        this.node.on(NodeEventType.SIZE_CHANGED, this.onNodeSizeChanged, this)
        this.node.on(NodeEventType.NODE_DESTROYED, this.onNodeDestroy, this);
        const root = new GPanel(this.node);  // 要创建了GPanel后才能添加InputProcessor
        root.owner = this._owner;
        this._root = root;
        this.updateRootSize();
        if (this._clickable && !root.inputProcessor) {
            const inputProcessor = this.node.addComponent(InputProcessor);
            root.setInputProcessor(inputProcessor);
        }
        root.delayAddPartner();
    }

    private async loadAndCreate(): Promise<void> {
        if (!this.packageName || this.packageName.length <= 0) {
            throw new Error('please select a package first!');
        }

        if (!this.itemName || this.itemName.length <= 0) {
            throw new Error('please select a package item first!');
        }

        const url = `ui://${this.packageName}/${this.itemName}`;
        await UIPackageManager.load(this.packageName);
        if (this._isDestoryed || !this.node || !isValid(this.node)) {
            throw new Error('UIPanel is destoryed!');
        }

        this.initRoot();

        this._cmp = UIPackage.createObjectFromURL(url) as GComponent;
        this._cmp.name = this.itemName;
        this._root.addChild(this._cmp);
        this._loaded = true;
    }

    protected onDestroy(): void {
        this.node.off(NodeEventType.SIZE_CHANGED, this.onNodeSizeChanged, this)
        this._isDestoryed = true;
    }

    private updateRootSize(): void {
        const uiTransform = this.node.getComponent(UITransform);
        this._root.setSize(uiTransform.width, uiTransform.height);
        // 设置GPanel居中
        this._root.setPosition(-uiTransform.width / 2, -uiTransform.height / 2);
        this._root.handlePositionChanged();
    }

    private onNodeSizeChanged(): void {
        if (this._root)
            this.updateRootSize();
    }
}

